'use strict'

/**
 * Promise 状态
 * 状态转换规则：
 *   成功：PENDING -> FULFILLED
 *   失败：PENDING -> REJECTED
 */
const STATUS = {
  PENDING: 'PENDING',
  FULFILLED: 'FULFILLED',
  REJECTED: 'REJECTED',
}

const Utils = {
  runAsync: function (fn) {
    setTimeout(fn, 0)
  },
  isFunction: function (val) {
    return val && typeof val === 'function'
  },
  isObject: function (val) {
    return val && typeof val === 'object'
  },
  isPromise: function (val) {
    return val && val instanceof MyPromise
  },
  resolvePromise(promsie2, value, resolve, reject) {
    // 避免循环调用自身
    if (promsie2 === value) {
      return reject(
        new TypeError('Chaining cycle detected for promise #<Promise>')
      )
    }
    // 如果 value 为 MyPromise 实例，则展开之
    if (Utils.isPromise(value)) {
      value.then(resolve, reject)
    } else {
      // 普通值
      resolve(value)
    }
  },
}

class MyPromise {
  constructor(fn) {
    if (!Utils.isFunction(fn)) {
      throw new Error('fn must be a function')
    }
    try {
      // 同步调用传入的异步任务
      fn(this.resolve, this.reject)
    } catch (e) {
      // 若传入的异步任务内部发生异常，将当前 promise 实例状态设置为失败（REJECTED）
      this.reject(e)
    }
  }

  // promsie 状态
  status = STATUS.PENDING
  // 成功之后的值
  value = undefined
  // 失败后的原因
  reason = undefined
  // 成功回调
  successCallbacks = []
  // 失败回调
  failCallbacks = []

  // 设置 promise 状态为 FULFILLED，并将 value 设置为成功之后的值
  resolve = (value) => {
    // 若当前 MyPromise 实例已决议，阻止程序继续向下执行
    if (this.status !== STATUS.PENDING) return
    this.value = value
    this.status = STATUS.FULFILLED
    // 判断失败回调是否存在 如果存在 调用
    this.flushCallbacks(this.successCallbacks)
  }

  // 设置 promise 状态为 REJECTED，并将 reason 设置为失败后的原因
  reject = (reason) => {
    // 若当前 MyPromise 实例已决议，阻止程序继续向下执行
    if (this.status !== STATUS.PENDING) return
    this.reason = reason
    this.status = STATUS.REJECTED
    // 判断失败回调是否存在 如果存在 调用
    this.flushCallbacks(this.failCallbacks)
  }

  then = (onFullfilled, onRejected) => {
    if (onFullfilled && !Utils.isFunction(onFullfilled)) {
      throw new Error('onFullfilled must be a function')
    }
    if (onRejected && !Utils.isFunction(onRejected)) {
      throw new Error('onRejected must be a function')
    }
    // 默认成功回调，简单的返回成功值
    if (!onFullfilled) {
      onFullfilled = (value) => value
    }
    // 默认失败回调，简单的重新抛出错误
    if (!onRejected) {
      onRejected = (reason) => {
        throw reason
      }
    }

    // 调用传入的成功或失败回调函数，并返回新的 promise 以实现then方法的链式调用
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === STATUS.FULFILLED) {
        Utils.runAsync(() => {
          try {
            let x = onFullfilled(this.value)
            Utils.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      } else if (this.status === STATUS.REJECTED) {
        Utils.runAsync(() => {
          try {
            let x = onRejected(this.reason)
            Utils.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      } else {
        // STATUS.PENDING
        // 加入回调数组，等待 promise 状态改变后调用
        this.successCallbacks.push(() => {
          Utils.runAsync(() => {
            try {
              let x = onFullfilled(this.value)
              Utils.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.failCallbacks.push(() => {
          Utils.runAsync(() => {
            try {
              let x = onRejected(this.reason)
              Utils.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }

  // 调用then方法注册的回调函数（onFullfilled, onRejected）
  flushCallbacks = (callbacks) => {
    while (callbacks.length) callbacks.shift()()
  }

  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value)
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason
        })
      }
    )
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  static all(array) {
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function onFullfilled(key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (Utils.isPromise(current)) {
          // promise 对象
          current.then(
            (value) => onFullfilled(i, value),
            (reason) => reject(reason)
          )
        } else {
          // 普通值
          onFullfilled(i, array[i])
        }
      }
    })
  }

  // MyPromise.resolve静态函数
  static resolve(value) {
    // 如果传入参数是一个 MyPromise 实例，直接返回。否则，创建一个新的 MyPromise 实例，并将value作为 promise 决议成功之后的值
    if (Utils.isPromise(value)) return value
    return new MyPromise((resolve) => resolve(value))
  }
}

module.exports = MyPromise
